using System.Buffers;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using SuperSocket.Channel;
using SuperSocket.ProtoBase;

namespace SuperSocket.Client.Proxy;

internal sealed class Socks5Request : ProxyRequest<Socks5Pack>
{
    private readonly string _username;
    private readonly string _password;
    private readonly EndPoint _remoteEndPoint;
    private readonly IChannel<Socks5Pack> _channel;
    private readonly IAsyncEnumerator<Socks5Pack> _packStream;
    private static readonly byte[] EndPointHeader = { 0x05, 0x01, 0x00 };
    private static readonly ReadOnlyMemory<byte> HandshakeRequest = new byte[] { 0x05, 0x02, 0x00, 0x02 };

    public Socks5Request(IChannel<Socks5Pack> channel,
        IAsyncEnumerator<Socks5Pack> packStream,
        EndPoint remoteEndPoint,
        string username,
        string password)
    {
        _channel = channel;
        _packStream = packStream;
        _username = username;
        _password = password;
        _remoteEndPoint = remoteEndPoint;
    }

    public override async ValueTask<Socks5Pack> SendHandshakeAsync()
    {
        await _channel.SendAsync(HandshakeRequest);
        return await _packStream.ReceiveAsync();
    }

    public override async ValueTask<Socks5Pack> SendAuthenticateAsync()
    {
        await _channel.SendAsync(writer =>
        {
            const byte v1  = 0x01;

            writer.WriteLittleEndian(v1);
            writer.WriterStringWithLength(_username, Encoding.ASCII);
            writer.WriterStringWithLength(_password, Encoding.ASCII);
        });

        return await _packStream.ReceiveAsync();
    }

    public override async ValueTask<Socks5Pack> SendEndPointAsync()
    {
        await _channel.SendAsync(write =>
        {
            int port;

            write.Write(EndPointHeader);

            if (_remoteEndPoint is IPEndPoint ipEndPoint)
            {
                port = ipEndPoint.Port;
                if (ipEndPoint.AddressFamily == AddressFamily.InterNetwork)
                {
                    const byte v1 = 0x01;
                    write.WriteLittleEndian(v1);
                    write.Write(ipEndPoint.Address.GetAddressBytes());
                }
                else if (ipEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    const byte v1 = 0x04;
                    write.WriteLittleEndian(v1);
                    write.Write(ipEndPoint.Address.GetAddressBytes());
                }
                else
                {
                    throw new Exception("unknown address family");
                }
            }
            else
            {
                const byte v1 = 0x03;

                var endPoint = (DnsEndPoint)_remoteEndPoint;

                port = endPoint.Port;

                write.WriteLittleEndian(v1);
                write.WriterStringWithLength(endPoint.Host, Encoding.ASCII);
            }

            write.WriteBigEndian((ushort)port);
        });

        return await _packStream.ReceiveAsync();
    }

    //private static byte[] GetEndPointBytes(EndPoint remoteEndPoint)
    //{
    //    var targetEndPoint = remoteEndPoint;

    //    byte[] buffer;
    //    int actualLength;
    //    int port;

    //    if (targetEndPoint is IPEndPoint)
    //    {
    //        var endPoint = targetEndPoint as IPEndPoint;
    //        port = endPoint.Port;

    //        if (endPoint.AddressFamily == AddressFamily.InterNetwork)
    //        {
    //            buffer = new byte[10];
    //            buffer[3] = 0x01;
    //            Buffer.BlockCopy(endPoint.Address.GetAddressBytes(), 0, buffer, 4, 4);
    //        }
    //        else if (endPoint.AddressFamily == AddressFamily.InterNetworkV6)
    //        {
    //            buffer = new byte[22];
    //            buffer[3] = 0x04;

    //            Buffer.BlockCopy(endPoint.Address.GetAddressBytes(), 0, buffer, 4, 16);
    //        }
    //        else
    //        {
    //            throw new Exception("unknown address family");
    //        }

    //        actualLength = buffer.Length;
    //    }
    //    else
    //    {
    //        var endPoint = (DnsEndPoint)targetEndPoint;

    //        port = endPoint.Port;

    //        var maxLen =
    //            7 + endPoint.Host.Length; //修改 var maxLen = 7 + Encoding.ASCII.GetMaxByteCount(endPoint.Host.Length);

    //        buffer = new byte[maxLen];

    //        buffer[3] = 0x03;
    //        buffer[4] = (byte)endPoint.Host.Length; //原来为0 现在为端口的长度
    //        actualLength = 5;
    //        actualLength += Encoding.ASCII.GetBytes(endPoint.Host, 0, endPoint.Host.Length, buffer, actualLength);
    //        actualLength += 2;
    //    }

    //    buffer[0] = 0x05;
    //    buffer[1] = 0x01;
    //    buffer[2] = 0x00;

    //    buffer[actualLength - 2] = (byte)(port / 256);
    //    buffer[actualLength - 1] = (byte)(port % 256);

    //    return buffer;
    //}

    //private static ArraySegment<byte> GetPassAuthenBytes(string username, string password)
    //{
    //    var buffer = new byte[3 + Encoding.ASCII.GetMaxByteCount(username.Length) +
    //                          (string.IsNullOrEmpty(password) ? 0 : Encoding.ASCII.GetMaxByteCount(password.Length))];

    //    buffer[0] = 0x01;
    //    var len = Encoding.ASCII.GetBytes(username, 0, username.Length, buffer, 2);

    //    buffer[1] = (byte)len;

    //    var actualLength = len + 2;

    //    if (!string.IsNullOrEmpty(password))
    //    {
    //        len = Encoding.ASCII.GetBytes(password, 0, password.Length, buffer, actualLength + 1);

    //        buffer[actualLength] = (byte)len;
    //        actualLength += len + 1;
    //    }
    //    else
    //    {
    //        buffer[actualLength] = 0x00;
    //        actualLength++;
    //    }

    //    return new ArraySegment<byte>(buffer, 0, actualLength);
    //}
}